#include <cctype>
#include <iostream>
#include <fstream>
#include <ostream>
#include "adspaudio.h"

#define DEBUG 1

using namespace std;

adspAudio::adspAudio(){
    i;
    j;
    filesize=0;
    SubChunk1Size=0;
    AudioFormat=0;
    NumChannels=0;
    SampleRate=0;
    ByteRate=0;
    BlockAlign=0;
    BitsPerSample=0;
    BytesPerSample=0;
    SubChunk2Size=0;
    duration = 0;
    durationLength = 0;
    data=0;   
    chunkoffset = 0;
    fail=false;       
}

void adspAudio::importFromWAV(string filename){
    ifstream file;
    unsigned char header[44];
  
    file.open(filename.c_str(), ios::binary);
    fail = true; // if any error is found, just return from this function
                  //   with _fail set
    if (file.fail())
        return;
    file.read((char *)header,12); 

    if (file.eof()) 
        return;
    // check RIFF
    if ( (header[0] != 'R' ) ||
         (header[1] != 'I' ) ||
         (header[2] != 'F' ) ||
         (header[3] != 'F' ) )
        return;  
    // check if WAVE format
    if ( (header[8] != 'W' ) ||
         (header[9] != 'A' ) ||
         (header[10] != 'V' ) ||
         (header[11] != 'E' ) ){
        cout << "This file is not in WAVE Format." << endl;
        return;}
    for (i=4; i<8; i++)
        filesize = ((header[7]*256+header[6])*256+header[5])*256+header[4]+8;
    file.read( (char *)header,4); // expecting subChunk1ID "fmt "
    if ( (header[0] != 'f' ) ||
         (header[1] != 'm' ) ||
         (header[2] != 't' ) ||
         (header[3] != ' ' ) ){
        cout << "This file is not in FMT." << endl;
        return;}
    file.read( (char *)header,4); // expecting subChunk1Size = 16
    SubChunk1Size = ((header[3]*256+header[2])*256+header[1])*256+header[0];
    if ( (SubChunk1Size > 0) && (SubChunk1Size<44) )  // array is only 44; rewrite if needed 
        file.read( (char *)header,SubChunk1Size);          // in supporting more formats
    else
        return;
    AudioFormat = header[1]*256+header[0];
    if (AudioFormat != 1){
        cout << "This file is not in PCM" << endl; // 1 means PCM WAV
        return;} // not supporting anything other than PCM
    NumChannels = header[3]*256+header[2];
    SampleRate = ((header[7]*256+header[6])*256+header[5])*256+header[4];   
    ByteRate = ((header[11]*256+header[10])*256+header[9])*256+header[8];
    BlockAlign = header[13]*256+header[12];
    BitsPerSample = header[15]*256+header[14];
    if ( (BitsPerSample != 8) && (BitsPerSample != 16)){
        cout << "This program only handles 8bit and 16bit WAV files only." << endl;
        return;} // we only know how to handle 8 and 16
    BytesPerSample = BitsPerSample/8;

    file.read( (char *)header,4); // expecting subChunk2ID "data"
    if ( (header[0] != 'd' ) ||
         (header[1] != 'a' ) ||
         (header[2] != 't' ) ||
         (header[3] != 'a' ) )
        return;
    file.read( (char *)header,4); // expecting subChunk1Size = 16
    SubChunk2Size = ((header[3]*256+header[2])*256+header[1])*256+header[0];

    duration = SubChunk2Size/BytesPerSample/NumChannels; 
    if(NumChannels == 1 && BytesPerSample == 1)
        durationLength = duration;
    else if(NumChannels == 1 && BytesPerSample == 2)
        durationLength = duration/2;
    else if(NumChannels == 2 && BytesPerSample == 1)
        durationLength = duration/(0.5);
    else if(NumChannels == 2 && BytesPerSample == 2)
        durationLength = duration/4;
    //durationLength = duration/(NumChannels*BytesPerSample);
    
    cout << "NumChannels: " << NumChannels << endl;
    cout << "Duration: " << duration << endl;
    cout << "Duration Length: " << durationLength << endl;
    durationLength = duration/(NumChannels*BytesPerSample);
    
    chunkoffset = file.tellg();
  
    data = new double * [NumChannels];
    for (i=0; i<NumChannels; i++){
        data[i] = new double [duration]; 
    }
  
    for (i=0; i<NumChannels; i++){
        if (data[i] == 0){
        cout << "Failed to Allocate Memory. \n";      
        }
    }
  
    for (i=0; i<duration; i++){
        for (j=0; j<NumChannels; j++){
            if (BytesPerSample == 1){
                file.read((char *)header,1); // (BytesPerSample == 1)
                data[j][i]=(header[0]-128)/128.0;    
            }
            else{
                file.read((char *)header,2); // (BytesPerSample == 2) 
                if (header[1] < 128)
                    data[j][i]= (header[1]*256.0+header[0])/32768.0;
                else
                    data[j][i]= (header[1]*256.0+header[0]-65536)/32768.0;
            }    
        }
    }      

    wavheader = new char[chunkoffset];
    file.seekg(0, ios::beg);
    file.read(wavheader, chunkoffset);
    file.close();
    fail=false;
}

void adspAudio::changeVolume(float volume){
    for(i = 0; i < NumChannels; i++){
        for(j = 0; j < duration; j++){
            data[i][j] = (data[i][j] * (volume/100.0));
        }    
    }
}

void adspAudio::mixAudio(double ** data1, double ** data2, int duration1, int duration2, int numChannel1, int numChannel2){
    if(numChannel1 == numChannel2){   
        for(i = 0; i < NumChannels; i++){
            for(j = 0; j < duration; j++){
                if(j <= duration1)
                {data[i][j] = (data1[i][j] + data2[i][j])/ 2;}
                else
                {data[i][j] = data2[i][j]/2;}
            }
        }
    }
    else if(numChannel1 < numChannel2){
        for(j = 0; j < duration; j++){
            if(j <= duration1)
                {data[0][j] = (data1[0][j] + data2[0][j] + data2[1][j])/3;} 
            else{
                data[0][j] = (data2[0][j] + data2[1][j]) / 2;
            }
        }
    }
    else{
        for(i = 0; i < NumChannels; i++){
            for(j = 0; j < duration; j++){
                if(j <= duration1){
                    //data[0][j] = (data1[0][j] + data2[0][j] / 2;
                    //data[1][j] = (data1[1][j] + data2[0][j] / 2;
                    data[i][j] = (data1[i][j] + data2[0][j]) / 2;
                } 
                else{
                    //data[0][j] = data2[0][j] / 2;
                    //data[1][j] = data2[0][j] / 2;
                    data[i][j] = data2[0][j] / 2;
                }
            }
        }
    }
}

double ** adspAudio::getData(){
    return data;
}

int adspAudio::getDuration(){
    return duration;
}

int adspAudio::getDurationLength(){
    return durationLength;
}
int adspAudio::getNumChannels(){
    return NumChannels;
}

int adspAudio::getSampleRate(){
    return SampleRate;
}

void adspAudio::exportToWAV(string filename){
    ofstream file;
    unsigned char header[44];
    file.open(filename.c_str(), ios::binary);
    if(file.fail()){
        cerr << "File Open Fail" << endl;
    }else{
        file.write(wavheader, chunkoffset);
        for(i = 0; i<duration; i++){
            for(j = 0; j<NumChannels; j++){
                if(BytesPerSample == 1){
                    file << (unsigned char)((data[j][i]*128.0) + 128.0);
                }
                else{
                    file << 2;
                    if (header[1] < 128){
                        file << (unsigned char)((data[j][i]*32768.0)/256.0);
                    }                   
                    else{
                        file << (unsigned char)((data[j][i]*32768.0 + 65536)/256.0);
                    }
                }
            }
        }
        file.close();
    }
}

void adspAudio::export_csv(string filename)
{
    ofstream file;
     
    file.open(filename.c_str(), ios::binary);

    if(file.fail()){
        cerr << "file open fail" << endl;
    }
    else{
        int i = 0;
        int j = 0;
        for(i=0; i<duration; i++){
            for(j=0; j<(NumChannels); j++){
                file << (data[j][i]) << "\n";
            }
        }    
    }
    file.close();
}

/*void adspAudio::export_csv(string filename){
     ofstream file;
     
     file.open(filename.c_str());

     if(file.fail()){
          cerr << "file open fail" << endl;
     }
     int i = 0, j = 0;
     for(i=0; i<duration; i++){
          for(j=0; j<(NumChannels); j++){
               if(j < (NumChannels - 1)){
                    if(BitsPerSample==8){
                        file << data[j][i] << "\t";
                    }else if(BitsPerSample==16 ){         
                         file << data[j][i] << "\t";
                    }
               }
               else if(j == (NumChannels - 1)){
                   if(BitsPerSample==8){
                        file << data[j][i] << endl;
                   }else if(BitsPerSample==16 ){         
                        file << data[j][i] << endl;
                   }
               }
          }
          
     }
     file.close();
}*/

bool adspAudio::error() {return fail;}
